﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

namespace BitmapTagger
{
	/// <summary>
	/// An Image Tagger whose property changes are observable
	/// </summary>
	public class ObservableBitmapTagger : INotifyPropertyChanged
	{
		#region NotifyPropertyChanged implementation

		/// <summary>
		/// Raised when a property is changed
		/// </summary>
		/// <remarks>
		/// Implemented from INotifyPropertyChanged
		/// </remarks>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Raise the PropertyChanged event, for the passed property name
		/// </summary>
		/// <param name="propertyName">The property that changed</param>
		protected void RaisePropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion

		/// <summary>
		/// Get or set the ImageTags
		/// </summary>
		private BitmapTagger ImageTags
		{
			get;
			set;
		}

		#region constructors

		/// <summary>
		/// Create a new Observable Image Tagger
		/// </summary>
		/// <remarks>
		/// Defaults with loading all tags
		/// Defaults with a Tagging System precedence of Exif > XMPExif > XMP
		/// </remarks>
		/// <param name="filePath">The file path to the image</param>
		public ObservableBitmapTagger(string filePath) 
		{
			ImageTags = BitmapTagger.Create(filePath);
		}

		/// <summary>
		/// Create a new Observable Image Tagger
		/// </summary>
		/// <param name="filePath">The file path to the image</param>
		/// <param name="loadExif">Whether or not to load Exif tags</param>
		/// <param name="loadXMPExif">Whether or not to load XMPExif tags</param>
		/// <param name="loadXMP">Whether or not to load XMP tags</param>
		/// <param name="precedence">The precedence of the different tagging systems (Exif, XMPExif, XMP)</param>
		public ObservableBitmapTagger(string filePath, bool loadExif, bool loadXMPExif, bool loadXMP, TagPrecedence precedence)
		{
			ImageTags = BitmapTagger.Create(filePath, loadExif, loadXMPExif, loadXMP, precedence);
		}

		/// <summary>
		/// Create a new ObservableImageTagger 
		/// </summary>
		/// <param name="imageTags">An ImageTagger for this object to make observable</param>
		public ObservableBitmapTagger(BitmapTagger imageTags)
		{
			ImageTags = imageTags;
		}

		#endregion

		#region save and load


		/// <summary>
		/// Save the image to the same file path it was loaded from
		/// </summary>
		/// <remarks>
		/// Defaults to save tags to Exif, XMPExif, and XMP
		/// </remarks>
		public void Save()
		{
			ImageTags.Save();
		}

		/// <summary>
		/// Save the image to the same file path it was loaded from
		/// </summary>
		/// <param name="saveExif">Whether or not to save the information to Exif tags</param>
		/// <param name="saveXMPExif">Whether or not to save the information to XMPExif tags</param>
		/// <param name="saveXMP">Whether or not to save the information to XMP tags</param>
		public void Save(bool saveExif, bool saveXMPExif, bool saveXMP)
		{
			ImageTags.Save(saveExif, saveXMPExif, saveXMP);
		}

		/// <summary>
		/// Save the image to a new file path
		/// </summary>
		/// <param name="filePath">The file path to save to</param>
		public void Save(string filePath)
		{
			ImageTags.Save(filePath);
		}


		/// <summary>
		/// Save the Image to a new file path
		/// </summary>
		/// <param name="filePath">The file path to save to</param>
		/// <param name="saveExif">Whether or not to save the information to Exif tags</param>
		/// <param name="saveXMPExif">Whether or not to save the information to XMPExif tags</param>
		/// <param name="saveXMP">Whether or not to save the information to XMP tags</param>
		public void Save(string filePath, bool saveExif, bool saveXMPExif, bool saveXMP)
		{
			ImageTags.Save(filePath, saveExif, saveXMPExif, saveXMP);
		}

		/// <summary>
		/// Load an Image
		/// </summary>
		/// <remarks>
		/// Defaults all tags to be loaded with a precedence of Exif > XMPExif > XMP
		/// </remarks>
		/// <param name="filePath">The file path of the Image to load</param>
		public void Load(string filePath)
		{
			ImageTags.Load(filePath);
		}

		/// <summary>
		/// Load an Image
		/// </summary>
		/// <param name="filePath">The file path of the Image to load</param>
		/// <param name="loadExif">Whether or not to load Exif tags</param>
		/// <param name="loadXMPExif">Whether or not to load XMPExif tags</param>
		/// <param name="loadXMP">Whether or not to load XMP tags</param>
		/// <param name="precendenceOfTags">The precendece of the tagging systems (used for conflicts)</param>
		public void Load(string filePath, bool loadExif, bool loadXMPExif, bool loadXMP,
			TagPrecedence precendenceOfTags)
		{
			ImageTags.Load(filePath, loadExif, loadXMPExif, loadXMP, precendenceOfTags);
		}

		#endregion


		/// <summary>
		/// Convert the Image Tagger's file to another format, the tags will be transfered also
		/// </summary>
		/// <param name="filePath">The filepath to the new image</param>
		/// <param name="transferExif">Whether or not to transfer the Exif data</param>
		/// <param name="transferXMPExif">Whether or not to transfer the XMP Exif data</param>
		/// <param name="transferXMP">Whether or not to transfer the XMP data</param>
		/// <returns>The Image Tagger for the new image</returns>
		/// <remarks>
		/// If filepath's extension is the same format as the current file path, this method is
		/// the same as the Save() method
		/// </remarks>
		public void ConvertImageType(string filePath, bool transferExif,
			bool transferXMPExif, bool transferXMP)
		{
			ImageTags = ImageTags.ConvertImageFormat(filePath, transferExif, transferXMPExif, transferXMP);
		}


		/// <summary>
		/// Get the file path to the image
		/// </summary>
		public string FilePath
		{
			get { return ImageTags.FilePath; }
		}



		#region image property tags


		/// <summary>
		/// Get the height of the image in pixels
		/// </summary>
		/// <ExifDefinition>
		/// The number of rows of image data. In JPEG compressed 
		/// data a JPEG marker is used instead of this tag. 
		/// </ExifDefinition>
		public uint? ImageHeight
		{
			get { return ImageTags.ImageHeight; }
		}


		/// <summary>
		/// Get the width of the image in pixels
		/// </summary>
		/// <ExifDefinition>
		/// The number of columns of image data, equal to the number of 
		/// pixels per row. In JPEG compressed data a JPEG marker is used 
		/// instead of this tag.   
		/// </ExifDefinition>
		public uint? ImageWidth
		{
			get { return ImageTags.ImageWidth; }
		}


		/// <summary>
		/// Get the X Resolution of the image
		/// </summary>
		/// <ExifDefinition>
		/// The number of pixels per ResolutionUnit in the ImageWidth direction. 
		/// When the image resolution is unknown, 72 [dpi] is designated. 
		/// </ExifDefinition>
		/// <value>
		/// Default = 72
		/// </value>
		public URational XResolution
		{
			get { return ImageTags.XResolution; }
		}


		/// <summary>
		/// Get the Y Resolution of the image
		/// </summary>
		/// <ExifDefinition>
		/// The number of pixels per  ResolutionUnit in the ImageLength direction. 
		/// The same value as  XResolution is designated. 
		/// </ExifDefinition>
		/// <value>
		/// Default = 72
		/// </value>
		public URational YResolution
		{
			get { return ImageTags.YResolution; }
		}


		/// <summary>
		/// Get the resolution unit of the image
		/// </summary>
		/// <ExifDefinition>
		/// The unit for measuring XResolution and YResolution. The same unit 
		/// is used for both XResolution and YResolution. If the image resolution 
		/// in unknown, 2 (inches) is designated. 
		/// </ExifDefinition>
		/// <value>
		/// 2 = inches 
		/// 3 = centimeters 
		/// Other = reserved 
		/// </value>
		public ushort? ResolutionUnit
		{
			get { return ImageTags.ResolutionUnit; }
		}

		/// <summary>
		/// Get the Height of the image in the resolution units.
		/// </summary>
		/// <remarks>
		/// This requires YResolution and ImageHeight to be present
		/// </remarks>
		public float? UnitHeight
		{
			get{ return ImageTags.UnitHeight;}
		}

		/// <summary>
		/// Get the Width of the image in the resolution units.
		/// </summary>
		/// <remarks>
		/// This requires XResolution and ImageWidth to be present
		/// </remarks>
		public float? UnitWidth
		{
			get{return ImageTags.UnitWidth;}
		}


		#endregion


		#region description tags


		/// <summary>
		/// Get or set the description for this image
		/// </summary>
		public string ImageDescription
		{
			get { return ImageTags.ImageDescription; }
			set 
			{ 
				ImageTags.ImageDescription = value;
				RaisePropertyChanged("ImageDescription");
			}
		}


		/// <summary>
		/// Get or set the maker of the equipment that made this image
		/// </summary>
		/// <note>
		/// This is Exif only, no XMP equivalant exists
		/// </note>
		/// <ExifDefinition>
		/// The manufacturer of the recording equipment. This is the manufacturer of 
		/// the DSC, scanner, video digitizer or other equipment that generated the 
		/// image. When the field is left blank, it is treated as unknown. 
		/// </ExifDefinition>
		public string Make
		{
			get { return ImageTags.Make; }
			set 
			{
				ImageTags.Make = value;
				RaisePropertyChanged("Make");
			}
		}


		/// <summary>
		/// Get or set the model of the equipment that made this image
		/// </summary>
		/// <ExifDefinition>
		/// The model name or model number of the equipment. This is 
		/// the model name of number of the DSC, scanner, video digitizer 
		/// or other equipment that generated the image. When the field 
		/// is left blank, it is treated as unknown. 
		/// </ExifDefinition>
		public string Model
		{
			get { return ImageTags.Model; }
			set 
			{
				ImageTags.Model = value;
				RaisePropertyChanged("Model");
			}
		}


		/// <summary>
		/// Get or set the software or firmware that generated this image
		/// </summary>
		/// <ExifDefinition>
		/// This tag records the name and version of the software or firmware 
		/// of the camera or image input device used togenerate the image. The 
		/// detailed format is not specified, but it is recommended that the example 
		/// shown below be followed. When the field is left blank, it is treated as unknown. 
		/// </ExifDefinition>
		/// <example>
		/// "Exif Software Version 1.00a" 
		/// </example>
		public string Software
		{
			get { return ImageTags.Software; }
			set 
			{
				ImageTags.Software = value;
				RaisePropertyChanged("Software");
			}
		}


		/// <summary>
		/// Get or set the image creator
		/// </summary>
		/// <ExifDefinition>
		/// This tag records the name of the camera owner, photographer or image creator. 
		/// The detailed format is not specified, but it is recommended that 
		/// the information be written as in the example below for ease of 
		/// Interoperability. When the field is left blank, it is treated as unknown. 
		/// </ExifDefinition>
		/// <example>
		/// "Camera owner, John Smith; Photographer, Michael Brown; Image creator, Ken James" 
		/// </example>
		public string Artist
		{
			get { return ImageTags.Artist; }
			set 
			{
				ImageTags.Artist = value;
				RaisePropertyChanged("Artist");
			}
		}



		/// <summary>
		/// Get or set the copyright information of this image
		/// </summary>
		/// <ExifDefinition>
		/// Copyright information. In this standard the tag is used to 
		/// indicate both the photographer and editor copyrights. It is the 
		/// copyright notice of the person or organization claiming rights 
		/// to the image. The Interoperability copyright statement including 
		/// date and rights should be written in  this field; e.g., 
		/// "Copyright, John Smith, 19xx. All rights reserved." In this 
		/// standard the field records both the photographer and editor copyrights, 
		/// with each recorded in a separate part of the statement. When there is a 
		/// clear distinction between the photographer and editor copyrights, these 
		/// are to be written in the order of photographer followed by editor copyright, 
		/// separated by NULL (in this case, since the statement also ends with a NULL, 
		/// there are two NULL codes) (see example 1). When only the photographer copyright 
		/// is given, it  is terminated by one NULL code (see example 2). When only the 
		/// editor copyright is given, the photographer copyright part consists of one space 
		/// followed by a terminating NULL code, then the editor copyright is given (see example 3). 
		/// When the field is left blank, it is treated as unknown. 
		/// </ExifDefinition>
		/// <example>
		/// When both the photographer copyright and editor copyright are given.   
		/// Photographer copyright + NULL[00.H] + editor copyright + NULL[00.H] 
		/// </example>
		/// <example>
		/// Ex. 2) When only the photographer copyright is given. 
		/// Photographer copyright + NULL[00.H] 
		/// </example>
		/// <example>
		/// Ex. 3) When only the editor copyright is given. 
		/// Space[20.H]+ NULL[00.H] + editor copyright + NULL[00.H] 
		/// </example>
		public string Copyright
		{
			get { return ImageTags.Copyright; }
			set 
			{
				ImageTags.Copyright = value;
				RaisePropertyChanged("Copyright");
			}
		}


		/// <summary>
		/// Get or set the user keywords or comments on the image
		/// </summary>
		/// <ExifDefinition>
		/// A tag for Exif users to write keywords or comments on the image 
		/// besides those in ImageDescription, and without the character 
		/// code limitations of the ImageDescription tag.   
		/// </ExifDefinition>
		public string UserComment
		{
			get { return ImageTags.UserComment; }
			set 
			{
				ImageTags.UserComment = value;
				RaisePropertyChanged("UserComment");
			}
		}


		/// <summary>
		/// Get or set the Manufacturer note for this image
		/// </summary>
		public string MakerNote
		{
			get { return ImageTags.MakerNote; }
			set 
			{
				ImageTags.MakerNote = value;
				RaisePropertyChanged("MakerNote");
			}
		}

		#endregion


		#region date and time tags


		/// <summary>
		/// Get or set the datetime of image creation/modify
		/// </summary>
		/// <ExifDefinition>
		/// The date and time of image creation. In this standard it  is the 
		/// date and time the file was changed. The format is 
		/// "YYYY:MM:DD HH:MM:SS" with time shown in 24-hour format, and the date and time 
		/// separated by one blank character [20.H]. When the date and time are 
		/// unknown, all  the character spaces except colons (":") may be filled 
		/// with blank characters, or else the Interoperability  field may be 
		/// filled with blank  characters. The character string length is 20 bytes 
		/// including NULL for termination. When the field is left blank, it is treated as unknown.   
		/// </ExifDefinition>
		/// <XmpDefinition>
		/// This is the datetime of last modification
		/// </XmpDefinition>
		public DateTime? DateTime
		{
			get { return ImageTags.DateTime; }
			set 
			{
				ImageTags.DateTime = value;
				RaisePropertyChanged("DateTime");
			}
		}


		/// <summary>
		/// Get or set the date and time the original image was generated
		/// </summary>
		/// <ExifDefinition>
		/// The date and time when the original image data was generated. 
		/// For a DSC the date and time the picture was taken are recorded. 
		/// The format is "YYYY:MM:DD HH:MM:SS" with time shown in 24-hour format, 
		/// and the date and time separated by one blank character [20.H]. When 
		/// the date and  time are unknown, all the character spaces except colons (":") 
		/// may be filled with blank characters, or else the Interoperability field may 
		/// be filled with blank characters. 
		/// The character string length is 20 bytes including NULL for termination. 
		/// When the field is left blank, it is treated as unknown.  
		/// </ExifDefinition>
		public DateTime? DateTimeOriginal
		{
			get { return ImageTags.DateTimeOriginal; }
			set 
			{
				ImageTags.DateTimeOriginal = value;
				RaisePropertyChanged("DateTimeOriginal");
			}
		}


		/// <summary>
		/// Get or set the date and time the image was stored as digital
		/// </summary>
		public DateTime? DateTimeDigitized
		{
			get { return ImageTags.DateTimeDigitized; }
			set 
			{
				ImageTags.DateTimeDigitized = value;
				RaisePropertyChanged("DateTimeDigitized");
			}
		}


		#endregion


		#region picture-taking condition tags



		/// <summary>
		/// Get or set the metering mode used for the image
		/// </summary>
		/// <values>
		/// 0 = unknown 
		/// 1 = Average 
		/// 2 = CenterWeightedAverage 
		/// 3 = Spot 
		/// 4 = MultiSpot 
		/// 5 = Pattern 
		/// 6 = Partial 
		/// Other = reserved 
		/// 255 = other 
		/// </values>
		public ushort? MeteringMode
		{
			get { return ImageTags.MeteringMode; }
			set
			{
				ImageTags.MeteringMode = value;
				RaisePropertyChanged("MeteringMode");
			}
		}




		/// <summary>
		/// Get or set the exposure time in seconds
		/// </summary>
		/// <ExifDefinition>
		/// Exposure time, given in seconds (sec). 
		/// </ExifDefinition>
		public URational ExposureTime
		{
			get { return ImageTags.ExposureTime; }
			set 
			{
				ImageTags.ExposureTime = value;
				RaisePropertyChanged("ExposureTime");
			}
		}



		/// <summary>
		/// Get or set the F Number of when the image was taken
		/// </summary>
		/// <ExifDefinition>
		/// The F number. 
		/// </ExifDefinition>
		public URational FNumber
		{
			get { return ImageTags.FNumber; }
			set 
			{
				ImageTags.FNumber = value;
				RaisePropertyChanged("FNumber");
			}
		}



		/// <summary>
		/// Get or set the program used by the camera to set exposure when
		/// the image was taken
		/// </summary>
		/// <ExifDefinition>
		/// The class of the program used by the camera to set 
		/// exposure when the picture is taken. The tag values 
		/// are as follows.  
		/// </ExifDefinition>
		/// <value>
		/// 0 = Not defined 
		/// 1 = Manual 
		/// 2 = Normal program 
		/// 3 = Aperture priority 
		/// 4 = Shutter priority 
		/// 5 =  Creative program (biased toward depth of field) 
		/// 6 =  Action program (biased toward fast shutter speed) 
		/// 7 =  Portrait mode (for closeup photos with the background out of focus)   
		/// 8 =  Landscape mode (for landscape photos with the background in focus) 
		/// Other = reserved 
		/// </value>
		public ushort? ExposureProgram
		{
			get { return ImageTags.ExposureProgram; }
			set
			{
				ImageTags.ExposureProgram = value;
				RaisePropertyChanged("ExposureProgram");
			}
		}




		/// <summary>
		/// Get or set the ISO Speed and ISO Latitude of the camera
		/// </summary>
		/// <ExifDefinition>
		/// Indicates the ISO Speed and ISO Latitude of the 
		/// camera or input device as specified in ISO 12232. 
		/// </ExifDefinition>
		public ushort? ISOSpeedRatings
		{
			get { return ImageTags.ISOSpeedRatings; }
			set 
			{
				ImageTags.ISOSpeedRatings = value;
				RaisePropertyChanged("ISOSpeedRatings");
			}
		}



		/// <summary>
		/// Get or set the shutter speed of the camera when the image was generated
		/// </summary>
		/// <ExifDefinition>
		/// Shutter speed. The unit is the APEX (
		/// Additive System of Photographic Exposure) setting 
		/// (see Annex C of Exif 2.2 specification).   
		/// </ExifDefinition>
		public Rational ShutterSpeed
		{
			get { return ImageTags.ShutterSpeed; }
			set 
			{
				ImageTags.ShutterSpeed = value;
				RaisePropertyChanged("ShutterSpeed");
			}
		}



		/// <summary>
		/// Get or set the aperture
		/// </summary>
		/// <ExifDefinition>
		/// The lens aperture. The unit is the APEX (
		/// Additive System of Photographic Exposure) setting 
		/// (see Annex C of Exif 2.2 specification).  
		/// </ExifDefinition>
		public URational Aperture
		{
			get { return ImageTags.Aperture; }
			set 
			{
				ImageTags.Aperture = value;
				RaisePropertyChanged("Aperture");
			}
		}



		/// <summary>
		/// Get or set the brightness of the camera when this image was generated
		/// </summary>
		/// <ExifDefinition>
		/// The value of brightness. The unit is the APEX value. 
		/// Ordinarily it is given in the range of -99.99 to 99.99. Note that 
		/// if the numerator of the recorded value is FFFFFFFF.H, 
		/// Unknown shall be indicated. 
		/// </ExifDefinition>
		public Rational Brightness
		{
			get { return ImageTags.Brightness; }
			set
			{
				ImageTags.Brightness = value;
				RaisePropertyChanged("Brightness");
			}
		}



		/// <summary>
		/// Get or set the kind of light source for the image
		/// </summary>
		/// <value>
		/// 0 = unknown 
		/// 1 = Daylight 
		///	2 = Fluorescent 
		///	3  =  Tungsten (incandescent light) 
		///	4 = Flash 
		///	9 = Fine weather 
		///	10 = Cloudy weather 
		///	11 = Shade 
		///	12  =  Daylight fluorescent (D 5700 – 7100K) 
		///	13  =  Day white fluorescent (N 4600 – 5400K) 
		///	14  =  Cool white fluorescent (W 3900 – 4500K) 
		///	15  =  White fluorescent (WW 3200 – 3700K) 
		///	17  =  Standard light A 
		///	18  =  Standard light B 
		///	19  =  Standard light C 
		///	20 = D55 
		///	21 = D65 
		///	22 = D75 
		///	23 = D50 
		///	24  =  ISO studio tungsten 
		///	255  =  other light source 
		///	Other = reserved 
		/// </value>
		public ushort? LightSource
		{
			get { return ImageTags.LightSource; }
			set
			{
				ImageTags.LightSource = value;
				RaisePropertyChanged("LightSource");
			}
		}




		/// <summary>
		/// Get or set the Sensing Method
		/// </summary>
		/// <ExifDefinition>
		/// Indicates the image sensor type on the camera or input device
		/// </ExifDefinition>
		/// <value>
		/// 1 = Not defined 
		///	2  =  One-chip color area sensor 
		///	3  =  Two-chip color area sensor 
		///	4  =  Three-chip color area sensor 
		///	5  =  Color sequential area sensor 
		///	7 =  Trilinear sensor 
		///	8  =  Color sequential linear sensor 
		///	Other = reserved 
		/// </value>
		public ushort? SensingMethod
		{
			get { return ImageTags.SensingMethod; }
			set
			{
				ImageTags.SensingMethod = value;
				RaisePropertyChanged("SensingMethod");
			}
		}


		#endregion
	}
}
